package top.bulk.idgeneration.genertation;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import top.bulk.idgeneration.entity.SegmentId;
import top.bulk.idgeneration.service.IdGeneratorService;

import javax.annotation.Resource;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 号段模式 - 双缓存
 *
 * @author 散装java
 * @date 2024-08-07
 */
@Component("segmentIdGen")
@Slf4j
public class SegmentIdGen implements IDGen<Long> {
    /**
     * 业务类型，数据库配置的 biz_type 字段
     */
    private Integer bizType = 101;
    /**
     * 生成id的步长，借此可以实现不同的步长
     */
    private Integer delta = 1;
    /**
     * 双缓存 current 当前缓存区 ； next 下一个缓存区
     */
    protected volatile SegmentId current;
    protected volatile SegmentId next;
    /**
     * 是否正在加载下一个，防止重复加载
     */
    private volatile boolean isLoadingNext;
    private final Object lock = new Object();
    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Resource
    IdGeneratorService idGeneratorService;

    public SegmentIdGen() {
    }

    public SegmentIdGen(Integer bizType, Integer delta) {
        this.bizType = bizType;
        this.delta = delta;
    }

    @Override
    public Long nextId() {
        while (true) {
            // 初始化
            if (current == null) {
                loadCurrent();
                continue;
            }
            // 生成一个id
            long id = current.getCurrentId().addAndGet(delta);
            // 如果生成的id超过了当前号段最大值，那么就去加载一下新的号段
            if (id > current.getMaxId()) {
                // 如果流量很大，可能很多现成同时去 loadCurrent(),所以 synchronized
                loadCurrent();
            } else {
                // 如果超过了加载预知，那么就提前加载下一号段
                if (id >= current.getLoadingId()) {
                    log.debug("加载下一号段：当前:{},阈值：{}", id, current.getLoadingId());
                    loadNext();
                }
                return id;
            }
        }
    }

    /**
     * 加载当前的数据。如果流量很大，可能很多线程同时去 loadCurrent(),所以 synchronized
     */
    private synchronized void loadCurrent() {
        if (current == null || !current.useful()) {
            if (next == null) {
                this.current = idGeneratorService.getNextSegmentId(bizType);
            } else {
                current = next;
                next = null;
            }
        }
    }

    private void loadNext() {
        // 双重检查锁，确保只有一个线程去加载下一个 next
        if (next == null && !isLoadingNext) {
            synchronized (lock) {
                if (next == null && !isLoadingNext) {
                    isLoadingNext = true;
                    executorService.submit(() -> {
                        try {
                            // 无论获取下个segmentId成功与否，都要将isLoadingNext赋值为false
                            next = idGeneratorService.getNextSegmentId(bizType);
                        } finally {
                            isLoadingNext = false;
                        }
                    });
                }
            }
        }
    }

}
